/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.user.client;

import com.google.gwt.http.client.URL;

/**
 * The utility class for packing and parsing the url hash.
 * 
 * <pre>
 * For the format below, #{one_char_prefix}{identifier}/{param1/param2/param3...}
 * use this resolver to parse an identifier with parameters from an url hash.
 * </pre>
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class HashTokenizer {

  public static final String HASH_PREFIX = "#";
  public static final String ACTION_TOKEN_PREFIX = "@";
  public static final String PAGE_TOKEN_PREFIX = "/";
  public static final String HASH_PARAM_DELIMITER = "/";

  private static final String ACTION_HASH_PREFIX = "#@";
  private static final String PAGE_HASH_PREFIX = "#/";

  /**
   * Checks whether the hash is a action hash.
   * 
   * @param hash the hash
   * @return
   */
  public static boolean isActionHash(String hash) {
    return hash.startsWith(ACTION_HASH_PREFIX);
  }

  /**
   * Checks whether the hash is a page hash.
   * 
   * @param hash the hash
   * @return
   */
  public static boolean isPageHash(String hash) {
    return hash.startsWith(PAGE_HASH_PREFIX);
  }

  /**
   * Composes a action hash with the given identifier and the parameters.
   * 
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the action hash
   */
  public static String packActionHash(String identifier, String... params) {
    return packHash(ACTION_TOKEN_PREFIX, identifier, params);
  }

  /**
   * Composes a action token with the given identifier and the parameters.
   * 
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the action token
   */
  public static String packActionToken(String identifier, String... params) {
    return packToken(ACTION_TOKEN_PREFIX, identifier, params);
  }

  /**
   * Composes a hash with the given prefix, identifier and the parameters.
   * 
   * @param prefix the prefix
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the hash
   */
  private static String packHash(String prefix, String identifier, String... params) {
    return HASH_PREFIX + packToken(prefix, identifier, params);
  }

  /**
   * Composes a page hash with the given identifier and the parameters.
   * 
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the page hash
   */
  public static String packPageHash(String identifier, String... params) {
    return packHash(PAGE_TOKEN_PREFIX, identifier, params);
  }

  /**
   * Composes a page token with the given identifier and the parameters.
   * 
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the page token
   */
  public static String packPageToken(String identifier, String... params) {
    return packToken(PAGE_TOKEN_PREFIX, identifier, params);
  }

  /**
   * Composes a hash token with the given prefix, identifier and the parameters.
   * 
   * @param prefix the prefix
   * @param identifier the idenifier
   * @param params the parameters
   * 
   * @return the hash token
   */
  private static String packToken(String prefix, String identifier, String... params) {
    StringBuilder sb = new StringBuilder(prefix + identifier);
    for (int i = 0; i < params.length; i++) {
      sb.append(HASH_PARAM_DELIMITER).append(
          params[i].isEmpty() ? params[i] : URL.encodeQueryString(params[i]));
    }
    return sb.toString();
  }

  /**
   * Parses the hash parameters.
   * 
   * @return the hash parameters
   */
  public static String[] parseHashParams(String hashToken) {
    // skip one-char prefix with '#' or not
    int skipIndex = hashToken.startsWith("#") ? 2 : 1;
    int delimiterIndex = hashToken.indexOf(HASH_PARAM_DELIMITER, skipIndex);
    // has params
    if (delimiterIndex > 0) {
      String paramToken = hashToken.substring(delimiterIndex + 1);
      String[] tokens = paramToken.split(HASH_PARAM_DELIMITER);
      String[] params = new String[tokens.length];
      for (int i = 0; i < tokens.length; i++) {
        params[i] = tokens[i].isEmpty() ? tokens[i] : URL.decodeQueryString(tokens[i]);
      }
      return params;
    } else {
      return null;
    }
  }

  /**
   * Parses the identifier from the given token.
   * 
   * @param hashToken the hash or the token
   * @return the identifier
   */
  public static String parseIdentifier(String hashToken) {
    // skip one-char prefix with '#' or not
    int skipIndex = hashToken.startsWith("#") ? 2 : 1;
    int delimiterIndex = hashToken.indexOf(HASH_PARAM_DELIMITER, skipIndex);
    // has params
    if (delimiterIndex > 0) {
      return hashToken.substring(skipIndex, delimiterIndex);
    } else {
      return hashToken.substring(skipIndex);
    }
  }

}
